import { ActionSegment } from "../Actuators/ActionSegment";
import { ActionSpec } from "../Actuators/ActionSpec";
import { ActuatorManager } from "../Actuators/ActuatorManager";
import { ActionBuffers } from "../Actuators/IActionReceiver";
import { AgentInfo } from "../Agent";
import { SensorCompressionType } from "../Sensors/CompressionSpec";
import { ISensor } from "../Sensors/ISensor";
import { ObservationWriter } from "../Sensors/ObservationWriter";
import { IPolicy } from "./IPolicy";


/**
 * @en
 *  The Heuristic Policy uses a hard-coded Heuristic method
 * to take decisions each time the RequestDecision method is called.
 * 
 * @zh
 * 启发式策略使用硬编码的启发式方法
 * 每次调用 RequestDecision 方法时做出决策。
 */
export  class HeuristicPolicy implements IPolicy {
    private _actuatorManager: ActuatorManager;
    private _actionBuffers: ActionBuffers;
    private _done: boolean = false;
    private _decisionRequested: boolean = false;

    private _observationWriter: ObservationWriter = new ObservationWriter();
    private _nullList: number[] = [];

    constructor(actuatorManager: ActuatorManager, actionSpec: ActionSpec) {
        this._actuatorManager = actuatorManager;
        const numContinuousActions = actionSpec.numContinuousActions;
        const numDiscreteActions = actionSpec.numDiscreteActions;
        const continuousDecision = new ActionSegment(new Array<number>(numContinuousActions), 0, numContinuousActions);
        const discreteDecision = new ActionSegment(new Array<number>(numDiscreteActions), 0, numDiscreteActions);
        this._actionBuffers = new ActionBuffers(continuousDecision, discreteDecision);
    }

    public requestDecision(info: AgentInfo, sensors: ISensor[]): void {
        this.stepSensors(sensors);
        this._done = info.done;
        this._decisionRequested = true;
    }

    public decideAction(): ActionBuffers {
        if (!this._done && this._decisionRequested) {
            this._actionBuffers.clear();
            this._actuatorManager.applyHeuristic(this._actionBuffers);
        }
        this._decisionRequested = false;
        return this._actionBuffers;
    }

    public dispose(): void {
    }

    /**
     * @en
     * Run ISensor.Write or ISensor.GetCompressedObservation for each sensor 
     * The output is currently unused, but this makes the sensor usage consistent 
     * between training and inference.
     * 
     * @zh
     * 对每个传感器运行 ISensor.Write 或 ISensor.GetCompressedObservation
     * 输出当前未使用，但这使传感器使用在训练和推理之间保持一致。
     * 
     * 
     * @param sensors 
     */
    private stepSensors(sensors: ISensor[]): void {
        for (const sensor of sensors) {
            if (sensor.getCompressionSpec().sensorCompressionType === SensorCompressionType.None) {
                this._observationWriter.setTarget(this._nullList, sensor.getObservationSpec(), 0);
                sensor.write(this._observationWriter);
            } else {
                sensor.getCompressedObservation();
            }
        }
    }
}